package net.alteiar.images.metadata;

import net.alteiar.Array2D;
import net.alteiar.images.BasicImage;
import net.alteiar.transformation.TransformationFunction;

public class ImageInfo extends IImageInfo {
	private final BasicImage image;

	private final Array2D maps;
	private final int squareSize;

	public ImageInfo(ImageInfo info) {
		this.image = info.image;
		this.maps = new Array2D(info.maps);
		this.squareSize = info.squareSize;

	}

	public ImageInfo(BasicImage img, int squareSize) {
		image = img;

		this.squareSize = squareSize;
		maps = new Array2D(img.getWidth() / squareSize, img.getHeight()
				/ squareSize, 0);
	}

	public BasicImage getImage() {
		return this.image;
	}

	public void fill(int value) {
		maps.fill(value);
	}

	@Override
	public int getSquareSize() {
		return squareSize;
	}

	@Override
	public int getWidthSquare() {
		return maps.getWidth();
	}

	@Override
	public int getHeightSquare() {
		return maps.getHeight();
	}

	@Override
	public int getImageWidth() {
		return getSquareSize() * getWidthSquare();
	}

	@Override
	public int getImageHeight() {
		return getSquareSize() * getHeightSquare();
	}

	public void setBatchValueAt(int x, int y, int value) {
		int blockX = x / squareSize;
		int blockY = y / squareSize;

		maps.set(blockX, blockY, value);
		this.setChanged();
	}

	public void endBatchModification() {
		this.notifyObservers();
	}

	public void setBatchValue(int x, int y, int value) {
		maps.set(x, y, value);
		this.setChanged();
	}

	/**
	 * 
	 * @param x
	 *            - x position on orginal image
	 * @param y
	 *            - y position on original image
	 * @param value
	 */
	public void setValueAt(int x, int y, int value) {
		int blockX = x / squareSize;
		int blockY = y / squareSize;

		setValue(blockX, blockY, value);
	}

	public void setValueAt(int x, int y, int width, int height, int value) {
		int blockX = x / squareSize;
		int blockY = y / squareSize;

		setValue(blockX, blockY, width, height, value);
	}

	/**
	 * 
	 * @param x
	 *            - x position on orginal image
	 * @param y
	 *            - y position on original image
	 * @return the value of the block at the original image position
	 */
	@Override
	public int getValueAt(int x, int y) {
		int blockX = x / squareSize;
		int blockY = y / squareSize;
		return getValue(blockX, blockY);
	}

	@Override
	public int getValue(int x, int y) {
		return maps.get(x, y);
	}

	@Override
	public int[] getValues(TransformationFunction fct) {
		return maps.getReplacedValues(fct);
	}

	@Override
	public void getValues(int[] vals, TransformationFunction fct) {
		if (this.squareSize == 1) {
			maps.getReplacedValues(vals, fct);
		} else {
			int[] tmp = maps.getReplacedValues(fct);

			int width = squareSize * getWidthSquare();
			for (int i = 0; i < tmp.length; i++) {
				int line = (int) Math.floor(i / getWidthSquare());

				int idx = line * width;
				idx = idx + (i - (line * getWidthSquare()));

				for (int x = 0; x < squareSize; ++x) {
					for (int y = 0; y < squareSize; ++y) {
						vals[idx * squareSize + x + (y * width)] = tmp[i];
					}
				}
			}
		}
	}

	public void setValue(int x, int y, int value) {
		maps.set(x, y, value);
		this.setChanged();
		this.notifyObservers();
	}

	public void setValue(final int xBegin, final int yBegin, final int width,
			final int height, final int value) {

		for (int x = xBegin; x < (xBegin + width); x++) {
			for (int y = yBegin; y < (yBegin + height); y++) {
				maps.set(x, y, value);
			}
		}

		setChanged();
		notifyObservers();
	}
}
